/*
 * This mapper class is responsible for telling the port forwarder where to forward traffic.
 * Each server instance will have its own mapper class, containing a set of selection keys that can be used for
 * traffic coming and going from client or destination service.
 */
package port4j;

import java.nio.channels.SelectionKey;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 * @author vupont
 */
public class Mapper {
    // Maps client's socket channel keys to destination service socket keys.
    private ConcurrentHashMap clientMap;
    // Maps destination service keys to client socket keys
    private ConcurrentHashMap destMap;
    private String[] clients;
    private String[] dests;
    private ConcurrentHashMap<String,String> destMappings;
    
    /**
     * Constructor for the mapper class
     * @param client Client IP / Port for this forwarder instance <IP:Port>
     * @param dest Destination service IP / Port for this instance <IP:Port>
     */
    public Mapper(String[] clients, String[] dests) {
        this.clients = clients;
        this.dests = dests;
        this.clientMap = new ConcurrentHashMap<SelectionKey,SelectionKey>();
        this.destMap = new ConcurrentHashMap<SelectionKey,SelectionKey>();
        this.destMappings = new ConcurrentHashMap<String, String>();
        for(int i = 0; i<clients.length;i++) {
            this.destMappings.put(clients[i], dests[i]);
        }
    }
    
    public String getDestIPForClient(String clientIP) {
        String val = null;
        if(this.destMappings.get(clientIP) != null) {
            val = (String) this.destMappings.get(clientIP).split(":")[0];
        }
        if(val == null) {
            //Check for any global rules
            val = (String) this.destMappings.get("*:"+clientIP.split(":")[1]).split(":")[0];
        }
        return val;
    }
    
    public int getDestPortForClient(String clientIP) {
        int val = 0;
        if(this.destMappings.get(clientIP) != null) {
            val = Integer.parseInt((String) this.destMappings.get(clientIP).split(":")[1]);
        }
        if(val <= 0) {
            val = Integer.parseInt((String) this.destMappings.get("*:" + clientIP.split(":")[1]).split(":")[1]);
        }
        return val;
    }
    
    public static Set<String> getListenerList(String[] clients) {
        Set<String> uniquePorts = new HashSet<String>();
        for (String singleClient : clients) {
            uniquePorts.add(singleClient.split(":")[1]);
        }
        return uniquePorts;
    }
    
    public void saveNewKey(SelectionKey clientKey, SelectionKey destKey) {
        this.clientMap.put(clientKey, destKey);
        this.destMap.put(destKey, clientKey);
    }
    
    public SelectionKey getValueForKey(SelectionKey key) {
        SelectionKey retrievedKey;
        //First try in the client collection
        retrievedKey = (SelectionKey) this.clientMap.get(key);
        if(retrievedKey == null) {
            retrievedKey = (SelectionKey) this.destMap.get(key);
        }
        return retrievedKey;
    }
    
    /**
     * Returns the socket key required to retrieve this client's destination socket.
     * @param clientKey Selection key of the client for which we want to find the destination key.
     * @return 
     */
    public SelectionKey getKeyForClient(SelectionKey clientKey) {
        return (SelectionKey) this.clientMap.get(clientKey);
    }
    
    /**
     * Returns the key for the client socket which we want this destination to send to
     * @param destKey Selection key of the destination for which we want to find the client key
     * @return 
     */
    public SelectionKey getKeyForDestination(SelectionKey destKey) {
        return (SelectionKey) this.destMap.get(destKey);
    }
    
}
